/**
 * @file
 * @author  Tsin Smith <tsin.creator@gmail.com>
 *
 */

#ifndef __SCDL_PARSER_SYMBOL_HPP__
#define __SCDL_PARSER_SYMBOL_HPP__

#include <scdl/parser/common.hpp>
#include <scdl/parser/RefCnt.hpp>

#define safe_unref(sym)                 \
        do {                            \
                if (sym) sym->unref();  \
        } while(0)

namespace scdl {

        enum SymbolCategory {
                CATEGORY_TBD = 0,

                CATEGORY_ASSUMPTION,
                CATEGORY_DEFINITION,
                CATEGORY_INTERFACE,
                CATEGORY_RECORD,
                CATEGORY_IDENTIFIER_LIST,

                CATEGORY_TYPE,
                CATEGORY_PRODUCT,
                CATEGORY_INDUCTIVE,

                CATEGORY_EXPR_START,
                CATEGORY_BOOL,
                CATEGORY_NUMBER,
                CATEGORY_STRING,
                CATEGORY_COUNTER,
                CATEGORY_RANDOM_NUMBER,
                CATEGORY_PARAMETER,
                CATEGORY_LIST,
                CATEGORY_APPLICATION,
                CATEGORY_INDUCTIVE_CTOR,
                CATEGORY_EXPR_END
        };

        enum SymbolTypeLevel {
                LEVEL_ROOT = 1,
                LEVEL_GENERAL,
                LEVEL_CLASS,
                LEVEL_OBJECT,
                LEVEL_END
        };

        class Symbol;
        class Type;
        class InductiveCtor;

        class PARSER_DLLEXPORT Symbol : public RefCnt {
        public:
                Symbol();
                virtual ~Symbol();

                void setPosition(const char *file, int line, int col);
                void setPosition(Symbol *sym);
                const char *file();
                int line();
                int column();
                virtual SymbolCategory getCategory();

                virtual void setType(Symbol *sym);
                Symbol *getType(Symbol *expected = 0);
                virtual void determineType(Symbol *expected);
                SymbolTypeLevel getTypeLevel();

                virtual void prettyPrint(bool verbose = false);

        protected:
                char *filename;
                int _line, _col;

                Symbol *type;
                bool typed;
                SymbolTypeLevel level;
        };

        class PARSER_DLLEXPORT NamedSymbol : public Symbol {
        public:
                NamedSymbol();
                virtual ~NamedSymbol();

                virtual void prettyPrint(bool verbose = false);

                void setName(const char *newname);
                const char *getName();

        protected:
                char *name;
        };

        class PARSER_DLLEXPORT Type : public NamedSymbol {
        public:
                Type();
                virtual ~Type();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);
        };

        class PARSER_DLLEXPORT Product : public NamedSymbol {
        public:
                Product();
                virtual ~Product();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

                void setFactor(Symbol *left, Symbol *right);
                Symbol *leftFactor();
                Symbol *rightFactor();

        protected:
                Symbol *left, *right;
        };

        class PARSER_DLLEXPORT Boolean : public Symbol {
        public:
                Boolean();
                virtual ~Boolean();

                void setValue(bool newval);
                bool getValue();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

        protected:
                bool val;
        };

        class PARSER_DLLEXPORT Number : public Symbol {
        public:
                Number();
                virtual ~Number();

                void setValue(float newval);
                float getValue();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

        protected:
                char type;
                float val, step, end;
                int interval, timeout;
        };

        class PARSER_DLLEXPORT String : public Symbol {
        public:
                String();
                virtual ~String();

                void setValue(const char *str);
                const char *getValue();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

        protected:
                char *string;
        };

        class PARSER_DLLEXPORT SymbolList : public NamedSymbol {
        public:
                SymbolList();
                virtual ~SymbolList();

                void pushElement(Symbol *sym);
                void updateElement(int idx, Symbol *sym);
                int getNrElements();
                Symbol *getElement(int idx);

        protected:
                int size, capacity;
                Symbol **elements;
        };

        class PARSER_DLLEXPORT Application : public SymbolList {
        public:
                Application();
                virtual ~Application();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);
                virtual void determineType(Symbol *expected);

                Symbol *getApplier();
                int getNrArguments();
                Symbol *getArgument(int idx);
        };

        class PARSER_DLLEXPORT List : public SymbolList {
        public:
                List();
                virtual ~List();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);
                virtual void determineType(Symbol *expected);

                Symbol *getSort();
                int getNrArguments();
                Symbol *getArgument(int idx);
        };

        class Record;
        class PARSER_DLLEXPORT Identifier : public NamedSymbol {
        public:
                Identifier();
                virtual ~Identifier();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

                void setDef(Symbol *sym);
                Symbol *getDef();

                void setBinder(const char *binder);
                void setRecord(Record *record);
                const char *getBinder();
                Record *getRecord();

                void setInductiveCtorValue(int v);
                int getInductiveCtorValue();

        protected:
                SymbolCategory category;
                Symbol *def;
                char *binder;
                int indCtorValue;
        };

        class PARSER_DLLEXPORT Record : public SymbolList {
        public:
                Record();
                virtual ~Record();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

                Identifier *getEntry(int idx);
        };

        class PARSER_DLLEXPORT Parameter : public NamedSymbol {
        public:
                Parameter();
                ~Parameter();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);

                void setIndex(int idx);
                int getIndex();

        private:
                int index;
        };

        class PARSER_DLLEXPORT IdentifierList : public SymbolList {
        public:
                IdentifierList();
                virtual ~IdentifierList();

                virtual SymbolCategory getCategory();
                virtual void prettyPrint(bool verbose = false);
        };

}

#endif /* __SCDL_PARSER_SYMBOL_HPP__ */
